#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/evp.h>

#include "abcrypto.h"
#include "cipher.h"

static int init_ctx(void **ctx, uint8_t *key, uint32_t klen, uint8_t *iv,
		    uint16_t alg, bool isenc)
{
	EVP_CIPHER_CTX *cipher_ctx = NULL;
	const EVP_CIPHER *md = NULL;

	if (!(cipher_ctx = EVP_CIPHER_CTX_new())) {
		ERR_print_errors_fp(stderr);
		return -1;
	}

	switch (alg) {
	case ABCRYPTO_CIPHER_AES_128_ECB:
		md = EVP_aes_128_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_128_CBC:
		md = EVP_aes_128_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_128_CTR:

		md = EVP_aes_128_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_128_CFB:
		md = EVP_aes_128_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_128_OFB:
		md = EVP_aes_128_ofb();
		break;
	case ABCRYPTO_CIPHER_AES_192_ECB:
		md = EVP_aes_192_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_192_CBC:
		md = EVP_aes_192_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_192_CTR:
		md = EVP_aes_192_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_192_CFB:
		md = EVP_aes_192_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_192_OFB:
		md = EVP_aes_192_ofb();
		break;

	case ABCRYPTO_CIPHER_AES_256_ECB:
		md = EVP_aes_256_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_256_CBC:
		md = EVP_aes_256_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_256_CTR:
		md = EVP_aes_256_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_256_CFB:
		md = EVP_aes_256_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_256_OFB:
		md = EVP_aes_256_ofb();
		break;

	case ABCRYPTO_CIPHER_SM4_ECB:
		md = EVP_sm4_ecb();
		break;
	case ABCRYPTO_CIPHER_SM4_CBC:
		md = EVP_sm4_cbc();
		break;
	case ABCRYPTO_CIPHER_SM4_CTR:
		md = EVP_sm4_ctr();
		break;
	case ABCRYPTO_CIPHER_SM4_CFB:
		md = EVP_sm4_cfb128();
		break;
	case ABCRYPTO_CIPHER_SM4_OFB:
		md = EVP_sm4_ofb();
		break;

	default:
		break;
	}

	if (md == NULL) {
		ERR_print_errors_fp(stderr);
		EVP_CIPHER_CTX_free(cipher_ctx);
		return -1;
	}

	if (isenc) {
		if (EVP_EncryptInit_ex(cipher_ctx, md, NULL, key, iv) != 1) {
			ERR_print_errors_fp(stderr);
			EVP_CIPHER_CTX_free(cipher_ctx);
			return -1;
		}
	} else {
		if (EVP_DecryptInit_ex(cipher_ctx, md, NULL, key, iv) != 1) {
			ERR_print_errors_fp(stderr);
			EVP_CIPHER_CTX_free(cipher_ctx);
			return -1;
		}
	}

	// 设置不使用填充
	EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);

	*ctx = cipher_ctx;

	return 0;
}

static void free_ctx(void *ctx)
{
	EVP_CIPHER_CTX_free(ctx);
	return;
}

static int encrypt(void *ctx, uint8_t *iv, uint8_t *data, uint32_t len,
		   uint8_t *dst)
{
	uint32_t outlen;
	if (1 != EVP_EncryptUpdate(ctx, dst, &outlen, data, len)) {
		ERR_print_errors_fp(stderr);
		return -1;
	}

	return 0;
}

static int decrypt(void *ctx, uint8_t *iv, uint8_t *data, uint32_t len,
		   uint8_t *dst)
{
	uint32_t outlen;
	if (1 != EVP_DecryptUpdate(ctx, dst, &outlen, data, len)) {
		ERR_print_errors_fp(stderr);
		return -1;
	}

	return 0;
}

static int once_encrypt(uint8_t *key, uint8_t *iv, uint8_t *src, uint8_t *dst,
			uint32_t len, uint16_t alg)
{
	EVP_CIPHER_CTX *ctx;
	int outlen;
	const EVP_CIPHER *md = NULL;

	// 创建并初始化上下文
	if (!(ctx = EVP_CIPHER_CTX_new())) {
		ERR_print_errors_fp(stderr);
		return -1;
	}

	switch (alg) {
	case ABCRYPTO_CIPHER_AES_128_ECB:
		md = EVP_aes_128_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_128_CBC:
		md = EVP_aes_128_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_128_CTR:

		md = EVP_aes_128_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_128_CFB:
		md = EVP_aes_128_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_128_OFB:
		md = EVP_aes_128_ofb();
		break;
	case ABCRYPTO_CIPHER_AES_192_ECB:
		md = EVP_aes_192_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_192_CBC:
		md = EVP_aes_192_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_192_CTR:
		md = EVP_aes_192_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_192_CFB:
		md = EVP_aes_192_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_192_OFB:
		md = EVP_aes_192_ofb();
		break;

	case ABCRYPTO_CIPHER_AES_256_ECB:
		md = EVP_aes_256_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_256_CBC:
		md = EVP_aes_256_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_256_CTR:
		md = EVP_aes_256_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_256_CFB:
		md = EVP_aes_256_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_256_OFB:
		md = EVP_aes_256_ofb();
		break;
	case ABCRYPTO_CIPHER_SM4_ECB:
		md = EVP_sm4_ecb();
		break;
	case ABCRYPTO_CIPHER_SM4_CBC:
		md = EVP_sm4_cbc();
		break;
	case ABCRYPTO_CIPHER_SM4_CTR:
		md = EVP_sm4_ctr();
		break;
	case ABCRYPTO_CIPHER_SM4_CFB:
		md = EVP_sm4_cfb128();
		break;
	case ABCRYPTO_CIPHER_SM4_OFB:
		md = EVP_sm4_ofb();
		break;
	default:
		break;
	}

	if (md == NULL) {
		ERR_print_errors_fp(stderr);
		EVP_CIPHER_CTX_free(ctx);
		return -1;
	}

	// 初始化加密操作
	if (1 != EVP_EncryptInit_ex(ctx, md, NULL, key, iv)) {
		ERR_print_errors_fp(stderr);
		EVP_CIPHER_CTX_free(ctx);
		return -1;
	}

	// 设置不使用填充
	EVP_CIPHER_CTX_set_padding(ctx, 0);

	// 加密数据
	if (1 != EVP_EncryptUpdate(ctx, dst, &outlen, src, len)) {
		ERR_print_errors_fp(stderr);
		EVP_CIPHER_CTX_free(ctx);
		return -1;
	}

	// // 结束加密操作
	// if (1 != EVP_EncryptFinal_ex(ctx, dst + outlen, &outlen)) {
	// 	ERR_print_errors_fp(stderr);
	// 	EVP_CIPHER_CTX_free(ctx);
	// 	return -1;
	// }
	// ciphertext_len += outlen;

	// 清理上下文
	EVP_CIPHER_CTX_free(ctx);

	return 0;
}

static int once_decrypt(uint8_t *key, uint8_t *iv, uint8_t *src, uint8_t *dst,
			uint32_t len, uint16_t alg)
{
	EVP_CIPHER_CTX *ctx;
	int outlen;
	const EVP_CIPHER *md = NULL;

	// 创建并初始化上下文
	if (!(ctx = EVP_CIPHER_CTX_new())) {
		ERR_print_errors_fp(stderr);
		EVP_CIPHER_CTX_free(ctx);
		return -1;
	}

	switch (alg) {
	case ABCRYPTO_CIPHER_AES_128_ECB:
		md = EVP_aes_128_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_128_CBC:
		md = EVP_aes_128_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_128_CTR:

		md = EVP_aes_128_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_128_CFB:
		md = EVP_aes_128_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_128_OFB:
		md = EVP_aes_128_ofb();
		break;
	case ABCRYPTO_CIPHER_AES_192_ECB:
		md = EVP_aes_192_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_192_CBC:
		md = EVP_aes_192_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_192_CTR:
		md = EVP_aes_192_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_192_CFB:
		md = EVP_aes_192_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_192_OFB:
		md = EVP_aes_192_ofb();
		break;

	case ABCRYPTO_CIPHER_AES_256_ECB:
		md = EVP_aes_256_ecb();
		break;
	case ABCRYPTO_CIPHER_AES_256_CBC:
		md = EVP_aes_256_cbc();
		break;
	case ABCRYPTO_CIPHER_AES_256_CTR:
		md = EVP_aes_256_ctr();
		break;
	case ABCRYPTO_CIPHER_AES_256_CFB:
		md = EVP_aes_256_cfb();
		break;
	case ABCRYPTO_CIPHER_AES_256_OFB:
		md = EVP_aes_256_ofb();
		break;
	case ABCRYPTO_CIPHER_SM4_ECB:
		md = EVP_sm4_ecb();
		break;
	case ABCRYPTO_CIPHER_SM4_CBC:
		md = EVP_sm4_cbc();
		break;
	case ABCRYPTO_CIPHER_SM4_CTR:
		md = EVP_sm4_ctr();
		break;
	case ABCRYPTO_CIPHER_SM4_CFB:
		md = EVP_sm4_cfb128();
		break;
	case ABCRYPTO_CIPHER_SM4_OFB:
		md = EVP_sm4_ofb();
		break;
	default:
		break;
	}

	if (md == NULL) {
		ERR_print_errors_fp(stderr);
		EVP_CIPHER_CTX_free(ctx);
		return -1;
	}

	// 初始化解密操作
	if (1 != EVP_DecryptInit_ex(ctx, md, NULL, key, iv)) {
		ERR_print_errors_fp(stderr);
		EVP_CIPHER_CTX_free(ctx);
		return -1;
	}

	// 设置不使用填充
	EVP_CIPHER_CTX_set_padding(ctx, 0);

	// 解密数据
	if (1 != EVP_DecryptUpdate(ctx, dst, &outlen, src, len)) {
		EVP_CIPHER_CTX_free(ctx);
		ERR_print_errors_fp(stderr);
		return -1;
	}
	// // 结束解密操作
	// if (1 != EVP_DecryptFinal_ex(ctx, dst + outlen, &final_len)) {
	// 	ERR_print_errors_fp(stderr);
	// 	EVP_CIPHER_CTX_free(ctx);
	// 	return -1;
	// }
	// plaintext_len += final_len;

	// 清理上下文
	EVP_CIPHER_CTX_free(ctx);
	return 0;
}

static struct abcrypto_skcipher _openssl_cipher = {
	.init_ctx = init_ctx,
	.free_ctx = free_ctx,
	.encrypt = encrypt,
	.decrypt = decrypt,
	.once_encrypt = once_encrypt,
	.once_decrypt = once_decrypt
};

struct abcrypto_skcipher *openssl_cipher(void)
{
	return &_openssl_cipher;
}
